package com.faradel.pharmapp.managedbean;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

import org.apache.commons.lang.StringUtils;
import org.faradel.pharmapp.entities.Medication;
import org.faradel.pharmapp.entities.MedicationInventory;
import org.faradel.pharmapp.entities.Patient;
import org.faradel.pharmapp.entities.PatientOrder;
import org.faradel.pharmapp.entities.PharmaUser;
import org.faradel.pharmapp.entities.PrescriptionOrder;
import org.springframework.context.ApplicationContext;
import org.springframework.web.jsf.FacesContextUtils;

import com.faradel.pharmapp.service.MedicationInventoryServiceInterface;
import com.faradel.pharmapp.service.MedicationServiceInterface;
import com.faradel.pharmapp.service.PatientOrderServiceInterface;
import com.faradel.pharmapp.service.PatientServiceInterface;
import com.faradel.pharmapp.service.PrescriptionOrderServiceInterface;

@ManagedBean
@ViewScoped
public class PatientOrderBean implements Serializable
{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Resource
    PatientOrder patientOrder;

    @Resource
    Patient patient;

    @Resource
    Medication medication;

    @Resource
    PrescriptionOrder prescriptionOrder;

    private PharmaUser currentUser;

    // patientOrder id
    private String patientOrderId;
    // regulates message to display with event
    private String message;
    private String action;

    private String outOfStockMedication;

    private String medication1;
    private String quantity1;
    private String medication2;
    private String quantity2;
    private String medication3;
    private String quantity3;
    private String medication4;
    private String quantity4;
    private String medication5;
    private String quantity5;

    private List<Medication> medicationList;

    /**
     * Initialize
     */
    {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        // Patient instance
        ApplicationContext context = FacesContextUtils.getWebApplicationContext(facesContext);
        patient = (Patient) context.getBean("patient");

        Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();

        message = "";

        String pid = params.get("patientId");
        action = params.get("action");

        // get patientOrder instance
        patientOrder = (PatientOrder) context.getBean("patientOrder");

        if (!("create".equals(action)))
        {
            patientOrderId = params.get("patientOrderId");
            patientOrder = getPatientOrderServiceInterface().getPatientOrderById(Integer.parseInt(patientOrderId));

            assignMedicationValues(patientOrder);
            setOrderInSession();
        }
        else
        {
            int patientId = Integer.parseInt(pid);
            // get patient information
            patient = getPatientServiceInterface().getPatientById(patientId);

            patientOrder.setPatientFirstName(patient.getFirstName());
            patientOrder.setPatientLastName(patient.getLastName());
            patientOrder.setPatientId(patient.getPatientId());
        }

        // load all medications into cache
        medicationList = getMedicationServiceInterface().getAllMedications();

        // get PrescriptionOrder instance
        prescriptionOrder = (PrescriptionOrder) context.getBean("prescriptionOrder");

        // get Medication instance
        medication = (Medication) context.getBean("medication");

        // current user logged in
        setCurrentUser((PharmaUser) facesContext.getExternalContext().getSessionMap().get("sessionUser"));

    }

    /**
     * On find, update, delete assign list of medications
     * 
     * @return
     */
    private void assignMedicationValues(PatientOrder po)
    {
        Map<Integer, String> map = new HashMap<Integer, String>();
        int i = 1;

        for (PrescriptionOrder p : po.getPrescriptionOrders())
        {
            map.put(i, p.getMedication().getGenericName() + " " + p.getMedication().getDosageForm() + " Id:" + String.valueOf(p.getMedication().getMedicationId()) + ";" + String.valueOf(p.getQuantity()));
            i++;
        }

        if (StringUtils.isNotBlank(map.get(1)))
        {
            medication1 = map.get(1).substring(0, map.get(1).indexOf(';'));
            quantity1 = map.get(1).substring(map.get(1).indexOf(';') + 1);
        }

        if (StringUtils.isNotBlank(map.get(2)))
        {
            medication2 = map.get(2).substring(0, map.get(2).indexOf(';'));
            quantity2 = map.get(2).substring(map.get(2).indexOf(';') + 1);
        }

        if (StringUtils.isNotBlank(map.get(2)))
        {
            medication3 = map.get(3).substring(0, map.get(3).indexOf(';'));
            quantity3 = map.get(3).substring(map.get(3).indexOf(';') + 1);
        }

        if (StringUtils.isNotBlank(map.get(2)))
        {
            medication4 = map.get(4).substring(0, map.get(4).indexOf(';'));
            quantity4 = map.get(4).substring(map.get(4).indexOf(';') + 1);
        }

        if (StringUtils.isNotBlank(map.get(2)))
        {
            medication5 = map.get(5).substring(0, map.get(5).indexOf(';'));
            quantity5 = map.get(5).substring(map.get(5).indexOf(';') + 1);
        }

    }

    private PatientServiceInterface getPatientServiceInterface()
    {
        ApplicationContext context = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
        return (PatientServiceInterface) context.getBean("patientServiceImpl");
    }

    private PatientOrderServiceInterface getPatientOrderServiceInterface()
    {
        ApplicationContext context = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
        return (PatientOrderServiceInterface) context.getBean("patientOrderServiceImpl");
    }

    private PrescriptionOrderServiceInterface getPrescriptionOrderServiceInterface()
    {
        ApplicationContext context = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
        return (PrescriptionOrderServiceInterface) context.getBean("prescriptionOrderServiceImpl");
    }

    private PrescriptionOrder getPrescriptionOrderInstance()
    {
        ApplicationContext context = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
        return (PrescriptionOrder) context.getBean("prescriptionOrder");
    }

    private MedicationServiceInterface getMedicationServiceInterface()
    {
        ApplicationContext context = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
        return (MedicationServiceInterface) context.getBean("medicationServiceImpl");
    }

    private MedicationInventoryServiceInterface getMedicationInventoryServiceInterface()
    {
        ApplicationContext context = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
        return (MedicationInventoryServiceInterface) context.getBean("medicationInventoryServiceImpl");
    }

    /**
     * create a new patient order
     */
    public void createPatientOrder()
    {

        List<PrescriptionOrder> medlist = createPrescriptionList();
        patientOrder.setPrescriptionOrders(medlist);
        patientOrder.setOrderDate(TimeClass.getCurrentDate());
        patientOrder.setLastModified(TimeClass.getCurrentDate());
        patientOrder.setUserLastModified(currentUser.getPharmaUserId());

        try
        {
            getPatientOrderServiceInterface().addPatientOrder(patientOrder);

            setPatientOrderId(String.valueOf(patientOrder.getPatientOrderId()));
            setOrderInSession();

            for (PrescriptionOrder p : medlist)
            {
                p.setPatientOrder(patientOrder);
                getPrescriptionOrderServiceInterface().addPrescriptionOrder(p);
            }
            FacesMessage facesMessage = null;

            if ("completed".equalsIgnoreCase(reserveMedication(medlist)))
            {
                setMessage("ordercreated");
            }
            else
            {
                setMessage("outofstock");
                facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Message", "The medication " + outOfStockMedication + "is out of stock!!!");
                FacesContext.getCurrentInstance().addMessage(null, facesMessage);
            }

        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

    }

    private void setOrderInSession()
    {
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        Map<String, Object> sessionMap = externalContext.getSessionMap();
        sessionMap.put("orderId", patientOrderId);
    }

    /**
     * Update an existing patient order
     */
    public void updatePatientOrder()
    {
        List<PrescriptionOrder> medlist = createPrescriptionList();
        patientOrder.setPrescriptionOrders(medlist);
        patientOrder.setOrderDate(TimeClass.getCurrentDate());
        patientOrder.setLastModified(TimeClass.getCurrentDate());
        patientOrder.setUserLastModified(currentUser.getPharmaUserId());

        try
        {
            getPatientOrderServiceInterface().updatePatientOrder(patientOrder);

            setPatientOrderId(String.valueOf(patientOrder.getPatientOrderId()));

            ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
            Map<String, Object> sessionMap = externalContext.getSessionMap();
            sessionMap.put("orderId", patientOrderId);

            for (PrescriptionOrder p : medlist)
            {
                p.setPatientOrder(patientOrder);
                getPrescriptionOrderServiceInterface().updatePrescriptionOrder(p);
            }
            FacesMessage facesMessage = null;

            if ("completed".equalsIgnoreCase(reserveMedication(medlist)))
            {
                setMessage("orderupdated");
            }
            else
            {
                setMessage("outofstock");
                facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Message", "The medication " + outOfStockMedication + "is out of stock!!!");
                FacesContext.getCurrentInstance().addMessage(null, facesMessage);
            }

        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

    }

    /**
     * Delete an exising patient Order
     */
    public void deletePatientOrder()
    {
        removeInventory();
        getPatientOrderServiceInterface().deletePatientOrder(patientOrder);
        setMessage("orderdeleted");
    }

    private void removeInventory()
    {
        // Return Inventory
        for (PrescriptionOrder po : patientOrder.getPrescriptionOrders())
        {
            MedicationInventory mi = getMedicationInventoryServiceInterface().getMedicationInventoryById(po.getMedication().getMedicationId());
            mi.setAvailableQuantity(mi.getAvailableQuantity() + po.getQuantity());
        }
    }

    /**
     * Get the list of medications on the prescription
     * 
     * @return
     */
    private List<PrescriptionOrder> createPrescriptionList()
    {
        List<PrescriptionOrder> pl = new ArrayList<PrescriptionOrder>();
        int quantity = 0;

        Map<Integer, String> map = getMedicationMap();

        // split string to get values needed for medication Id and quantity needed
        for (int i = 1; i <= map.size(); i++)
        {
            PrescriptionOrder po = getPrescriptionOrderInstance();
            int begin = map.get(i).indexOf(":");
            int end = map.get(i).indexOf(";");
            int medId = Integer.parseInt(map.get(i).substring(begin + 1, end));
            quantity = Integer.parseInt(map.get(i).substring(end + 1));

            po.setMedication(getMedicationServiceInterface().getMedicationById(medId));

            if (po.getQuantity() != quantity && po.getQuantity() != 0)
            {
                // Return inventory if quantity changed, will be reserved again with new quantity
                MedicationInventory mi = getMedicationInventoryServiceInterface().getMedicationInventoryById(po.getMedication().getMedicationId());
                mi.setAvailableQuantity(mi.getAvailableQuantity() + po.getQuantity());
            }
            po.setQuantity(quantity);
            po.setCost(getMedicationInventoryServiceInterface().getMedicationInventoryById(medId).getSellPrice());
            po.setLastModified(TimeClass.getCurrentDate());
            po.setUserLastModified(currentUser.getPharmaUserId());

            pl.add(po);
        }
        return pl;
    }

    /**
     * Reserve medications
     * 
     * @param medlist
     * @return
     */
    private String reserveMedication(List<PrescriptionOrder> medlist)
    {
        for (PrescriptionOrder p : medlist)
        {
            int id = p.getMedication().getMedicationId();
            MedicationInventory medInv = getMedicationInventoryServiceInterface().getMedicationInventoryById(id);

            // get quantity
            int quantity = medInv.getReservedQuantity();

            // reserve quantity
            medInv.setReservedQuantity(quantity + p.getQuantity());
            if (medInv.getReorderQuantity() > medInv.getAvailableQuantity())
            {
                setOutOfStockMedication(getMedicationServiceInterface().getMedicationById(id).getGenericName());
                return "outofstock";
            }
            else
            {
                medInv.setAvailableQuantity(medInv.getAvailableQuantity() - medInv.getReservedQuantity());
            }

            getMedicationInventoryServiceInterface().updateMedicationInventory(medInv);
        }
        return "completed";
    }

    private Map<Integer, String> getMedicationMap()
    {
        Map<Integer, String> map = new HashMap<Integer, String>();

        if (StringUtils.isNotBlank(medication1))
            map.put(1, medication1 + ";" + quantity1);

        if (StringUtils.isNotBlank(medication2))
            map.put(2, medication2 + ";" + quantity2);

        if (StringUtils.isNotBlank(medication3))
            map.put(3, medication3 + ";" + quantity3);

        if (StringUtils.isNotBlank(medication4))
            map.put(4, medication4 + ";" + quantity4);

        if (StringUtils.isNotBlank(medication5))
            map.put(5, medication5 + ";" + quantity5);

        return map;
    }

    public String next()
    {
        return "processprecriptionpayment?faces-redirect=true";
    }

    /**
     * Autocomplete method for medication
     * 
     * @param query
     * @return
     */
    public List<String> autoComplete(String query)
    {
        List<String> result = new ArrayList<String>();
        for (Medication m : medicationList)
        {
            int x = query.length();

            if (query.equalsIgnoreCase(m.getGenericName().substring(0, x)))
                result.add(m.getGenericName() + " " + m.getDosageForm() + " Id:" + m.getMedicationId());
        }
        return result;
    }

    public PatientOrder getPatientOrder()
    {
        return patientOrder;
    }

    public void setPatientOrder(PatientOrder patientOrder)
    {
        this.patientOrder = patientOrder;
    }

    public Patient getPatient()
    {
        return patient;
    }

    public void setPatient(Patient patient)
    {
        this.patient = patient;
    }

    public List<Medication> getMedicationList()
    {
        return medicationList;
    }

    public void setMedicationList(List<Medication> medicationList)
    {
        this.medicationList = medicationList;
    }

    public String getMedication1()
    {
        return medication1;
    }

    public void setMedication1(String medication1)
    {
        this.medication1 = medication1;
    }

    public String getQuantity1()
    {
        return quantity1;
    }

    public void setQuantity1(String quantity1)
    {
        this.quantity1 = quantity1;
    }

    public String getMedication2()
    {
        return medication2;
    }

    public void setMedication2(String medication2)
    {
        this.medication2 = medication2;
    }

    public String getQuantity2()
    {
        return quantity2;
    }

    public void setQuantity2(String quantity2)
    {
        this.quantity2 = quantity2;
    }

    public String getMedication3()
    {
        return medication3;
    }

    public void setMedication3(String medication3)
    {
        this.medication3 = medication3;
    }

    public String getQuantity3()
    {
        return quantity3;
    }

    public void setQuantity3(String quantity3)
    {
        this.quantity3 = quantity3;
    }

    public String getMedication4()
    {
        return medication4;
    }

    public void setMedication4(String medication4)
    {
        this.medication4 = medication4;
    }

    public String getQuantity4()
    {
        return quantity4;
    }

    public void setQuantity4(String quantity4)
    {
        this.quantity4 = quantity4;
    }

    public String getMedication5()
    {
        return medication5;
    }

    public void setMedication5(String medication5)
    {
        this.medication5 = medication5;
    }

    public String getQuantity5()
    {
        return quantity5;
    }

    public void setQuantity5(String quantity5)
    {
        this.quantity5 = quantity5;
    }

    public Medication getMedication()
    {
        return medication;
    }

    public void setMedication(Medication medication)
    {
        this.medication = medication;
    }

    public PrescriptionOrder getPrescriptionOrder()
    {
        return prescriptionOrder;
    }

    public void setPrescriptionOrder(PrescriptionOrder prescriptionOrder)
    {
        this.prescriptionOrder = prescriptionOrder;
    }

    public PharmaUser getCurrentUser()
    {
        return currentUser;
    }

    public void setCurrentUser(PharmaUser currentUser)
    {
        this.currentUser = currentUser;
    }

    public String getMessage()
    {
        return message;
    }

    public void setMessage(String message)
    {
        this.message = message;
    }

    public String getOutOfStockMedication()
    {
        return outOfStockMedication;
    }

    public void setOutOfStockMedication(String outOfStockMedication)
    {
        this.outOfStockMedication = outOfStockMedication;
    }

    public String getPatientOrderId()
    {
        return patientOrderId;
    }

    public void setPatientOrderId(String patientOrderId)
    {
        this.patientOrderId = patientOrderId;
    }

    public String getAction()
    {
        return action;
    }

    public void setAction(String action)
    {
        this.action = action;
    }

}
